package com.artup.util;

import java.beans.Introspector;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
 
public class ClassUtil {
	private static Logger log = LoggerFactory.getLogger(ClassUtil.class);

	// 缓存
	private static final Map<String, Field[]> cache = new java.util.concurrent.ConcurrentHashMap<String, Field[]>();

	/**
	 * Add jar file into classpath.
	 * 
	 * @param jarFile
	 * @param addMainfestClasspath
	 *            tell the load if load the class-path in the mainfest file.
	 * @throws IOException
	 */
	public static void addJarFileToClasspath(File jarFile,
			boolean addMainfestClasspath) throws IOException {
		List<URL> urls = new ArrayList<URL>(5);
		urls.add(jarFile.toURI().toURL());

		if (addMainfestClasspath) {
			JarFile jf = new JarFile(jarFile);
			Manifest mf = jf.getManifest(); // if jar has a class-path in
											// manfist
											// add it's entries. That means we
											// should get it's dependency jar
											// before
											// add this jar to classpath
			if (mf != null) {
				String cp = mf.getMainAttributes().getValue("class-path");
				if (cp != null) {
					for (String cpe : cp.split("\\s+")) {
						File lib = new File(jarFile.getParentFile(), cpe);
						urls.add(lib.toURI().toURL());
					}
				}
			}
			jf.close();
		}

		URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
		Class<? extends URLClassLoader> sysclass = URLClassLoader.class;
		try {
			Method method = sysclass.getDeclaredMethod("addURL", URL.class);
			method.setAccessible(true);
			for (URL url : urls) {
				method.invoke(sysloader, url);
			}
		} catch (Throwable t) {
			throw new IOException(
					"Error, could not add URL to system classloader", t);
		}
	}

	/**
	 * 创建一个对象实例
	 * 
	 * @param clazz
	 * @return T
	 * @throws Exception
	 */
	public static <T> T instantiate(final Class<T> clazz) throws Exception {
		Constructor<T> constructorToUse = null;
		try {
			if (System.getSecurityManager() != null) {
				constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<T>>() {
							@Override
							public Constructor<T> run() throws Exception {
								return clazz.getDeclaredConstructor((Class[]) null);
							}
						});
			} else {
				constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
			}
		} catch (Exception ex) {
			throw new Exception("No default constructor found");
		}
		return BeanUtils.instantiateClass(constructorToUse);
	}
	
	/**
	 * 创建对象实例
	 * 
	 * @param clazz
	 * @param parameterTypes
	 * @param args
	 * @return T
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public static <T> T instantiate(final Class<T> clazz, final Class[] parameterTypes, Object[] args) throws Exception {
		Constructor<T> constructorToUse = null;
		try {
			if (System.getSecurityManager() != null) {
				constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<T>>() {
							@Override
							public Constructor<T> run() throws Exception {
								return clazz.getDeclaredConstructor(parameterTypes);
							}
						});
			} else {
				constructorToUse = clazz.getDeclaredConstructor(parameterTypes);
			}
		} catch (Exception ex) {
			throw new Exception("No default constructor found");
		}
		return BeanUtils.instantiateClass(constructorToUse, args);
	}
	
	/**
	 * 创建Class
	 * 
	 * @param <T>
	 * @param name
	 * @return T
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> forName(String name)	throws ClassNotFoundException {
		try {
			return (Class<T>) Thread.currentThread().getContextClassLoader().loadClass(name);
		} catch (Exception e) {
			return (Class<T>) Class.forName(name);
		}
	}
	
	/**
	 * 拷贝属性
	 * 
	 * @param toBean
	 *            目标bean
	 * @param fromBean
	 *            源bean
	 */
	@SuppressWarnings("rawtypes")
	public static void copyPropertiesFromBean(Object fromBean, Object toBean) {
		try {
			Class clazz = toBean.getClass();
			Object value = null;
			//查找所有类的Field
			Field[] fields = getAllFieldsRecursive(clazz);
			for (Field field : fields) {
				String fieldName = field.getName();
				try{
					// 从fromBean得到值
					value = getBeanValue(fieldName, fromBean);
				}catch(Exception e){
					
				}
				if(value == null){
					continue;
				}
				if (value instanceof Integer && ((Integer) value).intValue() == 0) {
					continue;
				}
				if (value instanceof Long && ((Long) value).intValue() == 0) {
					continue;
				}
				if (value instanceof Double	&& ((Double) value).intValue() == 0) {
					continue;
				}
				if (value instanceof BigDecimal	&& ((BigDecimal) value).intValue() == 0) {
					continue;
				}
				if (value instanceof String	&& StringUtils.isBlank((String) value)) {
					continue;
				}
				
				setValueForBean(toBean, fieldName, value);
			}
		} catch (Exception e) {
			log.error(e.getMessage());
		}
	}

	/**
	 * 把value值设置到对象 bean中fieldName字段上
	 * 
	 * @param bean
	 * @param fieldName
	 * @param value
	 */
	@SuppressWarnings("rawtypes")
	public static void setValueForBean(Object bean, String fieldName, Object value) {
		try {
			if (fieldName == null || bean == null) {
				return;
			}
			fieldName = fieldName.trim();
			Class clazz = bean.getClass();
			Field f = findFieldRecursive(clazz, fieldName);
			if (f == null) {
				return;
			}
			f.setAccessible(true);
			Class ftype = f.getType();
			// 如果是原始类型
			if (ftype.isPrimitive() && value.getClass().getName().equals("java.lang.String")) {
				value = Integer.parseInt((String) value);
			}
			if (ftype.getName().equals("java.util.Date")) {
				if (value instanceof String) {
					String originDate = (String) value;
					value = CommonUtils.dateFormat(originDate);
				}
			}
			f.set(bean, value);
		} catch (Exception e) {
			log.error(e.getMessage());
		}
	}

	/**
	 * 执行bean的setter方法
	 * 
	 * @param bean
	 * @param fieldName
	 * @param value
	 */
	public static void executeSetter(Object bean, String fieldName, Object value) {
		executeSetter(bean, fieldName, value, null);
	}

	/**
	 * 执行 setter 方法
	 * 
	 * @param bean
	 * @param fieldName
	 * @param value
	 * @param type
	 */
	@SuppressWarnings("rawtypes")
	public static void executeSetter(Object bean, String fieldName, Object value, Class<?> type) {
		if(value == null){
			return;
		}
		try {
			if (fieldName == null || bean == null) {
				return;
			}
			fieldName = StringUtils.capitalize(fieldName);
			String methodName = "set" + fieldName;
			Class<?> clazz = bean.getClass();
			Method method = null;
			if (type != null) {
				method = clazz.getMethod(methodName, type);
			} else {
				method = findMethodByName(clazz, methodName);
			}
			// method.setAccessible(true);
			if ((!Modifier.isPublic(method.getModifiers()) || !Modifier
					.isPublic(method.getDeclaringClass().getModifiers()))
					&& !method.isAccessible()) {
				method.setAccessible(true);
			}
			
			Class[] parameterTypes = method.getParameterTypes();
			if(parameterTypes != null && parameterTypes.length > 0){
				String parameterTypeName = parameterTypes[0].getName();
				if(parameterTypeName.equals("boolean") || parameterTypeName.equals("java.lang.Boolean")){
					if(value instanceof String){
						value = Boolean.valueOf((String)value);
					}  
				} else if(parameterTypeName.equals("int") || parameterTypeName.equals("java.lang.Integer")){
					if(value instanceof String){
						value = Integer.valueOf((String)value);
					} else if(value instanceof Float){
						value = ((Float)value).intValue();
					} else if(value instanceof Double){
						value = ((Double)value).intValue();
					}
				} else if(parameterTypeName.equals("float") || parameterTypeName.equals("java.lang.Float")){
					if(value instanceof String){
						value = Float.valueOf((String)value);
					} else if(value instanceof Double){
						value = ((Double)value).floatValue();
					} else if(value instanceof Integer){
						value = ((Integer)value).floatValue();
					}

				} else if(parameterTypeName.equals("double") || parameterTypeName.equals("java.lang.Double")){
					if(value instanceof String){
						value = Double.valueOf((String)value);
					} else if(value instanceof Float){
						value = ((Float)value).doubleValue();
					} else if(value instanceof Integer){
						value = ((Integer)value).doubleValue();
					}
				} else if(parameterTypeName.equals("Date") || parameterTypeName.equals("java.util.Date")){
					Date date = null;
					try {
						if(value instanceof String) {
							SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
							date = sdf.parse((String) value);
						} else if(value instanceof Date){
							date = (Date)value;
						}
					} catch (ParseException ex) {
						log.error(ex.getMessage());
					}
					value = date; 
				}
			}

			method.invoke(bean, value);
		} catch (Exception e) {
			log.error("set property : " + fieldName + " Error, cause : " + e.getMessage());
		}
	}

	/**
	 * 根据方法名称，递归查询出此方法。 先查询当前类，如果没有查询父类。直到Object类为止。 类中不能有同名的函数，否则会查找失败，默认会找到第一个
	 * 
	 * @param clazz
	 * @param methodName
	 * @return method
	 */
	public static Method findMethodByName(Class<?> clazz,	String methodName) {
		if (clazz == null || clazz.getName().equals("java.lang.Object")) {
			return null;
		}
		Method[] methods = clazz.getDeclaredMethods();
		if (methods == null || methods.length == 0) {
			return findMethodByName(clazz.getSuperclass(), methodName);
		}
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if (method.getName().equals(methodName)) {
				return method;
			}
		}
		return findMethodByName(clazz.getSuperclass(), methodName);
	}

	/**
	 * 递归查找指定类的字段
	 * 
	 * @param clazz
	 *            类的Class
	 * @param fieldName
	 *            字段名
	 * @return field
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	@SuppressWarnings("rawtypes")
	public static Field findFieldRecursive(Class clazz, String fieldName) throws SecurityException, NoSuchFieldException {
		if (clazz == null || fieldName == null) {
			return null;
		}
		Field[] fields = clazz.getDeclaredFields();
		if (fields == null) {
			return null;
		}
		for (Field f : fields) {
			if (f.getName().equals(fieldName)) {
				return f;
			}
		}
		if (!clazz.getSuperclass().getName().equals("java.lang.Object")) {
			return findFieldRecursive(clazz.getSuperclass(), fieldName);
		}
		return null;
	}

	/**
	 * 递归的获取当前类的所有属性
	 * 
	 * @param clazz
	 * @return field数组
	 */
	public static Field[] getAllFieldsRecursive(Class<?> clazz) {
		if (clazz == null) {
			return null;
		}
		Field[] fields = cache.get(clazz.getName());
		if (fields == null) {
			fields = clazz.getDeclaredFields();
		}
		if (fields == null) {
			return null;
		}
		Field[] parentField = null;
		if (!clazz.getSuperclass().getName().equals("java.lang.Object")) {
			parentField = getAllFieldsRecursive(clazz.getSuperclass());
		}
		if (parentField == null) {
			return fields;
		}
		Field[] newFields = cache.get("all." + clazz.getName());
		if (newFields == null) {
			int flen = fields.length;
			int plen = parentField.length;
			newFields = new Field[flen + plen];
			System.arraycopy(fields, 0, newFields, 0, flen);
			System.arraycopy(parentField, 0, newFields, flen, plen);
		}

		return newFields;
	}

	/**
	 * 获取bean对象的fieldName属性的值（调用get方法）
	 * 
	 * @param fieldName
	 * @param bean
	 * @return object
	 */
	@SuppressWarnings({ "rawtypes" })
	public static Object getBeanValue(String fieldName, Object bean) {
		try {
			String methodName = "get" + StringUtils.capitalize(fieldName);
			Class clazz = bean.getClass();
			Method m = findMethodWithNoArgs(clazz, methodName);
			if (m == null) {
				methodName = "is" + StringUtils.capitalize(fieldName);
				m = findMethodWithNoArgs(clazz, methodName);
			}
			if (m == null) {
				return null;
			}
			m.setAccessible(true);
			return m.invoke(bean, new Object[] {});
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return null;
	}

	/**
	 * 递归查找指定类的方法
	 * 
	 * @param clazz
	 *            类的Class
	 * @param methodName
	 *            方法名
	 * @return method
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws NoSuchMethodException
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Method findMethodWithNoArgs(Class clazz, String methodName) {
		if (clazz == null || methodName == null) {
			return null;
		}
		Method m = null;
		try {
			m = clazz.getDeclaredMethod(methodName, new Class[] {});
		} catch (NoSuchMethodException e) {
			log.error(e.getMessage());
		}
		if (m != null) {
			return m;
		}
		if (!clazz.getSuperclass().getName().equals("java.lang.Object")) {
			return findMethodWithNoArgs(clazz.getSuperclass(), methodName);
		}
		
		return null;
	}

	/**
	 * 递归查找类及其父类所有属性
	 * 
	 * @param clazz
	 * @param fields
	 * @return field数组
	 */
	@SuppressWarnings("rawtypes")
	public static Field[] findParentFields(Class clazz, Field[] fields) {
		if (clazz == null) {
			return fields;
		}
		if (fields == null) {
			fields = new Field[0];
		}
		Field[] tmp = clazz.getDeclaredFields();
		if (tmp == null) {
			return fields;
		}
		int srcLen = fields.length;
		int tmpLen = tmp.length;
		Field[] newArry = new Field[srcLen + tmpLen];
		System.arraycopy(fields, 0, newArry, 0, srcLen);
		System.arraycopy(tmp, 0, newArry, srcLen, tmpLen);
		if (clazz.getSuperclass().getName().equals("java.lang.Object")) {
			return newArry;
		}
		return findParentFields(clazz.getSuperclass(), newArry);
	}
 
	/**
	 * 编译java源代码
	 * 
	 * @param javaSourceFile
	 * @param options
	 * @return boolean
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static boolean dynamicCompileJava(String javaSourceFile,	Iterable<String> options) {
		// targetDir = StringUtils.isBlank(targetDir) ?
		// System.getProperty("user.dir") + File.separator + "temp"
		// : targetDir;

		// 取得当前系统的编译器
		JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();

		// 获取一个文件管理器
		StandardJavaFileManager javaFileManager = javaCompiler
				.getStandardFileManager(null, null, null);

		// 文件管理器根与文件连接起来
		Iterable it = javaFileManager.getJavaFileObjects(new File(
				javaSourceFile));

		// 创建编译任务
		CompilationTask task = javaCompiler.getTask(null, javaFileManager,
				null, options, null, it);

		// 执行编译
		boolean compileResult = task.call();

		try {
			javaFileManager.close();
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return compileResult;
		// // 8.运行程序
		// Runtime run = Runtime.getRuntime();
		// Process process = run.exec("java -cp ./temp temp/com/Hello");
		// InputStream in = process.getInputStream();
		// BufferedReader reader = new BufferedReader(new
		// InputStreamReader(in));
		// String info = "";
		// while ((info = reader.readLine()) != null) {
		// System.out.println(info);
		//
		// }
	}
	
	/**
	 * 获取泛型对象
	 * 
	 * @param clazz
	 * @return Class
	 */
	@SuppressWarnings("rawtypes")
	public static Class getGenericType(Class clazz) {
		Type genericClass = clazz.getGenericSuperclass();
		if (genericClass != null) {
			return getType(genericClass);
		}

		// 从上级获取泛型
		Type[] types = clazz.getGenericInterfaces();
		for (Type type : types) {
			Class genType = getType(type);
			if (genType != null) {
				return genType;
			}
		}

		return null;
	}
	
	/**
	 * 获取泛型对象
	 * 
	 * @param genericClass
	 * @return Class
	 */
	@SuppressWarnings("rawtypes")
	public static Class getType(Type genericClass) {
		if (genericClass instanceof ParameterizedType) {
			ParameterizedType parmTypeImpl = (ParameterizedType) genericClass;
			Type[] act = parmTypeImpl.getActualTypeArguments();
			if (act == null || act.length == 0) {
				return null;
			}
			Type parmType = act[0];
			if (parmType instanceof Class) {
				return (Class) parmType;
			}
		}
		return null;
	}
	
	/**
	 * 获取beanClass的名称
	 * 
	 * @param beanClass
	 * @return 名称
	 */
	public static String beanName(Class<?> beanClass) {
		AnnotationMetadata metadata = new StandardAnnotationMetadata(beanClass,	true);
		Set<String> types = metadata.getAnnotationTypes();
		String beanName = null;
		for (String type : types) {
			AnnotationAttributes attributes = AnnotationUtil.attributesFor(metadata, type);
			if (isStereotypeWithNameValue(type,	metadata.getMetaAnnotationTypes(type), attributes)) {
				Object value = attributes.get("value");
				if (value instanceof String) {
					String strVal = (String) value;
					if (StringUtils.isNotBlank(strVal)) {
						if (beanName != null && !strVal.equals(beanName)) {
							throw new IllegalStateException("Stereotype annotations suggest inconsistent " + "component names: '" + beanName + "' versus '" + strVal + "'");
						}
						beanName = strVal;
					}
				}
			}
		}
		return StringUtils.isBlank(beanName) ? buildDefaultBeanName(beanClass.getName()) : beanName;
	}
	
	/**
	 * 是否是annatationType对象
	 * 
	 * @param annotationType
	 * @param metaAnnotationTypes
	 * @param attributes
	 * @return boolean
	 */
	public static boolean isStereotypeWithNameValue(String annotationType,	Set<String> metaAnnotationTypes, Map<String, Object> attributes) {

		boolean isStereotype = annotationType.equals("net.mars.mybatis.MarsMapper")
				|| (metaAnnotationTypes != null && metaAnnotationTypes.contains("net.mars.mybatis.MarsMapper"))
				|| annotationType.equals("javax.annotation.ManagedBean")
				|| annotationType.equals("javax.inject.Named");

		return (isStereotype && attributes != null && attributes.containsKey("value"));
	}

	/**
	 * 得到默认的bean名称
	 * 
	 * @param className
	 * @return bean名称
	 */
	public static String buildDefaultBeanName(String className) {
		String shortClassName = ClassUtils.getShortName(className);
		return Introspector.decapitalize(shortClassName);
	}
	
	/**
	 * 根据多个包名搜索class
	 * 例如: ScanClassUtils.scanPakcages("javacommon.**.*");
	 * @param basePackages 各个包名使用逗号分隔,各个包名可以有通配符
	 * @return List包含className
	 */
	@SuppressWarnings("all")
	public static List<String> scanPackages(String basePackages) throws IllegalArgumentException{
		List result = new ArrayList();
		if(StringUtils.isBlank(basePackages)){
			return result;
		}

		ResourcePatternResolver rl = new PathMatchingResourcePatternResolver();
		MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(rl); 

		String[] arrayPackages = basePackages.split(",");
		try {
			for(int j = 0; j < arrayPackages.length; j++) {
				String packageToScan = arrayPackages[j];
				String packagePart = packageToScan.replace('.', '/');
				String classPattern = "classpath*:/" + packagePart + "/**/*.class";
				Resource[] resources = rl.getResources(classPattern);
				for (int i = 0; i < resources.length; i++) {
					Resource resource = resources[i];
					MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);   
					String className = metadataReader.getClassMetadata().getClassName();
					result.add(className);
				}
			}
		}catch(Exception e) {
			new IllegalArgumentException("scan pakcage class error,pakcages:"+basePackages);
		}

		return result;
	}
}
